//===============================================================================
// Copyright (C) 2008 Charles River Media. All rights reserved.
// 
// Catmull-Clark subdivision surface viewer program
//
// The developer of the code is
// Gyorgy Antal <gyantal@gmail.com>
//
// Contributor(s):
// Laszlo Szirmay-Kalos <szirmay@iit.bme.hu>
//
// The software was developed for the chapter 
// "Fast Evaluation of Subdivision Surfaces on DirectX10 Graphics Hardware"
// of the book ShaderX 6: Advanced Rendering Techniques, 
// edited by Wolfgang Engel,
// the appropriate section edited by Tom Forsyth. 
//
// The files are provided without support, instruction, or implied warranty of any
// kind.  Charles River Media makes no guarantee of its fitness for a particular purpose and is
// not liable under any circumstances for any damages or loss whatsoever arising
// from the use or inability to use this file or items derived from it.
//===============================================================================
// File: ObjSceneLoader.cpp
//
// Description:
//===============================================================================


#include "stdafx.h"
#include "Obj.h"
#include "Base.h"

#define SCENE_EXTERN __declspec(dllexport)
#include "Scene.h"

#include <assert.h>

void CalculateTangents1();

int FindQuadIndex(int p_vert0, int p_vert1, int p_originalQuad)
{
	int candidateQuad = -1;
	for (int i=0; i < g_nQuads; i++)
	{
		if (i == p_originalQuad)
			continue;

		if ((g_quads[i].VertIndex0 == p_vert1 && g_quads[i].VertIndex1 == p_vert0) ||
			(g_quads[i].VertIndex1 == p_vert1 && g_quads[i].VertIndex0 == p_vert0) ||

			(g_quads[i].VertIndex1 == p_vert1 && g_quads[i].VertIndex3 == p_vert0) ||
			(g_quads[i].VertIndex3 == p_vert1 && g_quads[i].VertIndex1 == p_vert0) ||

			(g_quads[i].VertIndex3 == p_vert1 && g_quads[i].VertIndex2 == p_vert0) ||
			(g_quads[i].VertIndex2 == p_vert1 && g_quads[i].VertIndex3 == p_vert0) ||

			(g_quads[i].VertIndex2 == p_vert1 && g_quads[i].VertIndex0 == p_vert0) ||
			(g_quads[i].VertIndex0 == p_vert1 && g_quads[i].VertIndex2 == p_vert0))
		{
			if (candidateQuad != -1)
			{
				// it is a double check here, if an edge is found more than one times (except the p_originalQuad), it is a degenerate mesh
				assert(false);	
				exit(-1);
			}

			candidateQuad = i;
		}
	}

	return candidateQuad;
}

int FindValence(int p_vert)
{
	int valence = 0;
	for (int i=0; i < g_nQuads; i++)
	{
		if (g_quads[i].VertIndex0 == p_vert || 
			g_quads[i].VertIndex1 == p_vert || 
			g_quads[i].VertIndex2 == p_vert || 
			g_quads[i].VertIndex3 == p_vert)
			valence++;
	}
	return valence;
}

// get previous according to the CW (CounteWise) order
int GetPreviousVertex(int p_quadIndex, int p_vertIndex)
{
	int v1Ind = g_quads[p_quadIndex].VertIndex0;
	int v2Ind = g_quads[p_quadIndex].VertIndex1;
	int v3Ind = g_quads[p_quadIndex].VertIndex2;
	int v4Ind = g_quads[p_quadIndex].VertIndex3;

	if (p_vertIndex == v1Ind)	
	{
		return v3Ind;
	}
	else if (p_vertIndex == v2Ind)
	{
		return v1Ind;
	}
	else if (p_vertIndex == v3Ind)
	{
		return v4Ind;
	}
	else if (p_vertIndex == v4Ind)
	{
		return v2Ind;
	}
	return -1;
}

void GetNextTwoVertexIndices(int p_quadIndex, int p_vertIndex, int* p_out1, int* p_out2)
{
	int v1Ind = g_quads[p_quadIndex].VertIndex0;
	int v2Ind = g_quads[p_quadIndex].VertIndex1;
	int v3Ind = g_quads[p_quadIndex].VertIndex2;
	int v4Ind = g_quads[p_quadIndex].VertIndex3;

	if (p_vertIndex == v1Ind)
	{
		*p_out1 = v2Ind;
		*p_out2 = v4Ind;
		return;
	}
	else if (p_vertIndex == v2Ind)
	{
		*p_out1 = v4Ind;
		*p_out2 = v3Ind;
		return;
	}
	else if (p_vertIndex == v3Ind)
	{
		*p_out1 = v1Ind;
		*p_out2 = v2Ind;
		return;
	}
	else if (p_vertIndex == v4Ind)
	{
		*p_out1 = v3Ind;
		*p_out2 = v1Ind;
		return;
	}
	*p_out1 = -1;
	*p_out2 = -1;
}


void FillQuadAdjacentVerticesBuffer(int p_quadIndex)
{
	int v1Ind = g_quads[p_quadIndex].VertIndex0;
	int v2Ind = g_quads[p_quadIndex].VertIndex1;
	int v3Ind = g_quads[p_quadIndex].VertIndex2;
	int v4Ind = g_quads[p_quadIndex].VertIndex3;

	unsigned int val1 = g_vertValences[v1Ind];
	unsigned int val2 = g_vertValences[v2Ind];
	unsigned int val3 = g_vertValences[v3Ind];
	unsigned int val4 = g_vertValences[v4Ind];

	{
		// v1:
		int neighbour1Quad = FindQuadIndex(v3Ind, v1Ind, p_quadIndex);
		int v11Ind = GetPreviousVertex(neighbour1Quad, v1Ind);
		g_quadAdjacentVerticesInd[20 * p_quadIndex] = v11Ind;

		if (val1 >= 4)
		{
			int neighbour2Quad = FindQuadIndex(v1Ind, v11Ind, neighbour1Quad);
			int v12Ind = -1;
			int v13Ind = -1;
			GetNextTwoVertexIndices(neighbour2Quad, v11Ind, &v12Ind, &v13Ind);
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 1] = v12Ind;
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 2] = v13Ind;

			if (val1 >= 5)
			{
				int neighbour3Quad = FindQuadIndex(v1Ind, v13Ind, neighbour2Quad);
				int v14Ind = -1;
				int v15Ind = -1;
				GetNextTwoVertexIndices(neighbour3Quad, v13Ind, &v14Ind, &v15Ind);
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 3] = v14Ind;
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 4] = v15Ind;
			}
		}
	}

	{
		// v2:
		int neighbour1Quad = FindQuadIndex(v1Ind, v2Ind, p_quadIndex);
		int v21Ind = GetPreviousVertex(neighbour1Quad, v2Ind);
		g_quadAdjacentVerticesInd[20 * p_quadIndex + 5] = v21Ind;

		if (val2 >= 4)
		{
			int neighbour2Quad = FindQuadIndex(v2Ind, v21Ind, neighbour1Quad);
			int v22Ind = -1;
			int v23Ind = -1;
			GetNextTwoVertexIndices(neighbour2Quad, v21Ind, &v22Ind, &v23Ind);
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 6] = v22Ind;
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 7] = v23Ind;

			if (val2 >= 5)
			{
				int neighbour3Quad = FindQuadIndex(v2Ind, v23Ind, neighbour2Quad);
				int v24Ind = -1;
				int v25Ind = -1;
				GetNextTwoVertexIndices(neighbour3Quad, v23Ind, &v24Ind, &v25Ind);
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 8] = v24Ind;
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 9] = v25Ind;
			}
		}
	}

	{
		// v3:
		int neighbour1Quad = FindQuadIndex(v4Ind, v3Ind, p_quadIndex);
		int v31Ind = GetPreviousVertex(neighbour1Quad, v3Ind);
		g_quadAdjacentVerticesInd[20 * p_quadIndex + 10] = v31Ind;

		if (val3 >= 4)
		{
			int neighbour2Quad = FindQuadIndex(v3Ind, v31Ind, neighbour1Quad);
			int v32Ind = -1;
			int v33Ind = -1;
			GetNextTwoVertexIndices(neighbour2Quad, v31Ind, &v32Ind, &v33Ind);
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 11] = v32Ind;
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 12] = v33Ind;

			if (val3 >= 5)
			{
				int neighbour3Quad = FindQuadIndex(v3Ind, v33Ind, neighbour2Quad);
				int v34Ind = -1;
				int v35Ind = -1;
				GetNextTwoVertexIndices(neighbour3Quad, v33Ind, &v34Ind, &v35Ind);
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 13] = v34Ind;
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 14] = v35Ind;
			}
		}
	}

	{
		// v4:
		int neighbour1Quad = FindQuadIndex(v2Ind, v4Ind, p_quadIndex);
		int v41Ind = GetPreviousVertex(neighbour1Quad, v4Ind);
		g_quadAdjacentVerticesInd[20 * p_quadIndex + 15] = v41Ind;

		if (val4 >= 4)
		{
			int neighbour2Quad = FindQuadIndex(v4Ind, v41Ind, neighbour1Quad);
			int v42Ind = -1;
			int v43Ind = -1;
			GetNextTwoVertexIndices(neighbour2Quad, v41Ind, &v42Ind, &v43Ind);
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 16] = v42Ind;
			g_quadAdjacentVerticesInd[20 * p_quadIndex + 17] = v43Ind;

			if (val4 >= 5)
			{
				int neighbour3Quad = FindQuadIndex(v4Ind, v43Ind, neighbour2Quad);
				int v44Ind = -1;
				int v45Ind = -1;
				GetNextTwoVertexIndices(neighbour3Quad, v43Ind, &v44Ind, &v45Ind);
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 18] = v44Ind;
				g_quadAdjacentVerticesInd[20 * p_quadIndex + 19] = v45Ind;
			}
		}
	}

}


// convert the data from right handed coordanate system of OBJ to left handed of general DX
HRESULT  LoadModelData_OBJ(char* p_fullPath)
{
	g_nVerts = 0;
	g_nTextureUvs = 0;
	g_nNormals = 0;
	g_nTangents = 0;
	g_nQuads = 0;

	ObjParser* objParser = new ObjParser(p_fullPath);
	
	objParser->ParseFile();

	//g_vertices
	if (objParser->nvertex > MaxVertCount)
		exit(-1);

	g_nVerts = objParser->nvertex;
	g_nTextureUvs = objParser->ntexture;
	g_nNormals = objParser->nnormal;
	g_nTangents = objParser->nnormal;
	
	//g_quads
	int nTriangles = 0;
	int n5SidePolys = 0;
	g_nQuads = 0;
	for (int i = 0; i < objParser->npoly_vertex; i++)
	{
		if (objParser->poly_vertexInd[i] == -1)
		{
			g_quads[g_nQuads].VertIndex0 = objParser->poly_vertexInd[i -  4] - 1;
			g_quads[g_nQuads].VertIndex1 = objParser->poly_vertexInd[i -  3] - 1;
			g_quads[g_nQuads].VertIndex2 = objParser->poly_vertexInd[i -  1] - 1;
			g_quads[g_nQuads].VertIndex3 = objParser->poly_vertexInd[i -  2] - 1;

			g_quadTextureInd[g_nQuads].TexIndex0 = objParser->poly_textureInd[i -  4] - 1;
			g_quadTextureInd[g_nQuads].TexIndex1 = objParser->poly_textureInd[i -  3] - 1;
			g_quadTextureInd[g_nQuads].TexIndex2 = objParser->poly_textureInd[i -  1] - 1;
			g_quadTextureInd[g_nQuads].TexIndex3 = objParser->poly_textureInd[i -  2] - 1;

			g_quadNormalInd[g_nQuads].NormalIndex0 = objParser->poly_normalInd[i -  4] - 1;
			g_quadNormalInd[g_nQuads].NormalIndex1 = objParser->poly_normalInd[i -  3] - 1;
			g_quadNormalInd[g_nQuads].NormalIndex2 = objParser->poly_normalInd[i -  1] - 1;
			g_quadNormalInd[g_nQuads].NormalIndex3 = objParser->poly_normalInd[i -  2] - 1;

			g_quadTangentInd[g_nQuads].TangentIndex0 = objParser->poly_normalInd[i -  4] - 1;
			g_quadTangentInd[g_nQuads].TangentIndex1 = objParser->poly_normalInd[i -  3] - 1;
			g_quadTangentInd[g_nQuads].TangentIndex2 = objParser->poly_normalInd[i -  1] - 1;
			g_quadTangentInd[g_nQuads].TangentIndex3 = objParser->poly_normalInd[i -  2] - 1;

			if (g_quads[g_nQuads].VertIndex0 < 0 || g_quads[g_nQuads].VertIndex1 < 0 || g_quads[g_nQuads].VertIndex2 < 0|| g_quads[g_nQuads].VertIndex3 < 0)
			{
				nTriangles++;
				continue;	// !there is  a triangle in the data
			}
			if (i - 5 >= 0)
			{	
				if (objParser->poly_vertexInd[i -  5] != -1)	// there is a poligon with more than 4 verts
				{
					n5SidePolys++;
					continue; // exit(-1);
				}
			}
			g_nQuads++;
		}
	}

	assert(nTriangles == 0 && n5SidePolys == 0);
	if (nTriangles != 0 || n5SidePolys != 0)	// there is a triangle or a poly5 among them
	{
		assert(false);
		exit(-1);
	}




	for (int i=0; i < g_nTextureUvs; i++)
	{
		g_textureUvs[i] = D3DXVECTOR2(objParser->vertex_textures[i].X(), -1.0f*(objParser->vertex_textures[i].Y() - 1.0f));		
	}

	for (int i=0; i < g_nNormals; i++)
	{
		g_normals[i] = D3DXVECTOR3(objParser->vertex_normals[i].X(), objParser->vertex_normals[i].Y(), -1.0f * objParser->vertex_normals[i].Z());		
		g_tangents[i] = D3DXVECTOR3(objParser->vertex_normals[i].X(), objParser->vertex_normals[i].Y(), -1.0f * objParser->vertex_normals[i].Z());		
	}

	int nValenceLess3 = 0;
	int nValence3 = 0;
	int nValence4 = 0;
	int nValence5 = 0;
	int nValenceGreater5 = 0;
	for (int i = 0; i < g_nVerts; i++)
	{
		//g_vertices[i].Pos = D3DXVECTOR3(objParser->vertices[i].X(), objParser->vertices[i].Y(), -1.0f * objParser->vertices[i].Z() + 5.0f);
		g_vertices[i].Pos = D3DXVECTOR3(objParser->vertices[i].X(), objParser->vertices[i].Y(), -1.0f * objParser->vertices[i].Z());

		//g_vertValences[i] = 4;
		g_vertValences[i] = FindValence(i);
		if (g_vertValences[i] <= 2)
			nValenceLess3++;
		else if (g_vertValences[i] == 3)
			nValence3++;
		else if (g_vertValences[i] == 4)
			nValence4++;
		else if (g_vertValences[i] == 5)
			nValence5++;
		else
			nValenceGreater5++;
	}

	if (nValenceLess3 > 0 || nValenceGreater5 > 0)
	{
		//assert(false); // it is not handled by the program correctly
	}


	for (int i = 0; i < g_nQuads * 20; i++)
	{
		g_quadAdjacentVerticesInd[i] = 0;
	}
	for (int i = 0; i < g_nQuads; i++)
	{
		FillQuadAdjacentVerticesBuffer(i);
	}

	CalculateTangents1();

	return S_OK;
}


void CalculateTriangleTangent(const D3DXVECTOR3& p_v1, const D3DXVECTOR3& p_v2, const D3DXVECTOR3& p_v3,
							  const D3DXVECTOR2& p_uv1, const D3DXVECTOR2& p_uv2, const D3DXVECTOR2& p_uv3,
							  D3DXVECTOR3* p_tangent, D3DXVECTOR3* p_biTangent)
{
	float s1 = p_uv2.x - p_uv1.x;
	float s2 = p_uv3.x - p_uv1.x;
	float t1 = p_uv2.y - p_uv1.y;
	float t2 = p_uv3.y - p_uv1.y;

	D3DXVECTOR3 Q1 = p_v2 - p_v1;
	D3DXVECTOR3 Q2 = p_v3 - p_v1;

	float r = 1.0F / (s1 * t2 - s2 * t1);

	*p_tangent = r * (t2 * Q1 - t1 * Q2);		// sdir -- points to U
	*p_biTangent = r * (s1 * Q2 - s2 * Q1);		// tdir -- points to V
	


	// !!! I am not sure that we need to normalize the TBN vectors??
	D3DXVECTOR3 tangent = *p_tangent;
	D3DXVec3Normalize(p_tangent, &tangent);
	D3DXVECTOR3 biTangent = *p_biTangent;
	D3DXVec3Normalize(p_biTangent, &biTangent);
}

// this is the new notation:
//  1 ----  2
//  |    /  |
//  |  /    |
//  3 ----  4
void CalculateTangent1(int p_quadIndex)
{
	// first triangle: 1, 2, 3, second: 2, 4, 3
	D3DXVECTOR3 v1 = g_vertices[g_quads[p_quadIndex].VertIndex0].Pos;
	D3DXVECTOR3 v2 = g_vertices[g_quads[p_quadIndex].VertIndex1].Pos;
	D3DXVECTOR3 v3 = g_vertices[g_quads[p_quadIndex].VertIndex2].Pos;
	D3DXVECTOR3 v4 = g_vertices[g_quads[p_quadIndex].VertIndex3].Pos;

	D3DXVECTOR2 uv1 = g_textureUvs[g_quadTextureInd[p_quadIndex].TexIndex0];
	D3DXVECTOR2 uv2 = g_textureUvs[g_quadTextureInd[p_quadIndex].TexIndex1];
	D3DXVECTOR2 uv3 = g_textureUvs[g_quadTextureInd[p_quadIndex].TexIndex2];
	D3DXVECTOR2 uv4 = g_textureUvs[g_quadTextureInd[p_quadIndex].TexIndex3];

	D3DXVECTOR3 temp = g_normals[g_quadNormalInd[p_quadIndex].NormalIndex0] + 
		g_normals[g_quadNormalInd[p_quadIndex].NormalIndex1] +
		g_normals[g_quadNormalInd[p_quadIndex].NormalIndex2] +
		g_normals[g_quadNormalInd[p_quadIndex].NormalIndex3];

	D3DXVECTOR3 quadNormal;
	D3DXVec3Normalize(&quadNormal, &temp);


	D3DXVECTOR3 tangent, biTangent;
	CalculateTriangleTangent(v1, v2, v3, uv1, uv2, uv3, &tangent, &biTangent);
	
	// try other's and see if there is difference
	D3DXVECTOR3 tangent2, biTangent2;	
	CalculateTriangleTangent(v2, v3, v1, uv2, uv3, uv1, &tangent2, &biTangent2);
	D3DXVECTOR3 tangent3, biTangent3;	
	CalculateTriangleTangent(v3, v1, v2, uv3, uv1, uv2, &tangent3, &biTangent3);

	D3DXVECTOR3 tangentB1, biTangentB1;
	CalculateTriangleTangent(v2, v4, v3, uv2, uv4, uv3, &tangentB1, &biTangentB1);
	D3DXVECTOR3 tangentB2, biTangentB2;
	CalculateTriangleTangent(v4, v3, v2, uv4, uv3, uv2, &tangentB2, &biTangentB2);
	D3DXVECTOR3 tangentB3, biTangentB3;
	CalculateTriangleTangent(v3, v2, v4, uv3, uv2, uv4, &tangentB3, &biTangentB3);
	
	/*if (abs((tangent - tangent2).x) > 0.01)
		return;*/

	if (D3DXVec3Length(&(tangent - tangent2)) > 0.01)
		return;
	if (D3DXVec3Length(&(tangent - tangent3)) > 0.01)
		return;

	if (D3DXVec3Length(&(tangentB1 - tangentB2)) > 0.01)
		return;
	if (D3DXVec3Length(&(tangentB1 - tangentB3)) > 0.01)
		return;

	if (D3DXVec3Length(&(biTangent - biTangent2)) > 0.01)
		return;
	if (D3DXVec3Length(&(biTangent - biTangent3)) > 0.01)
		return;

	if (D3DXVec3Length(&(biTangentB1 - biTangentB2)) > 0.01)
		return;
	if (D3DXVec3Length(&(biTangentB1 - biTangentB3)) > 0.01)
		return;

	
	D3DXVECTOR3 finalTangent;
	D3DXVec3Normalize(&finalTangent, &(tangent + tangentB1));


	g_tangents[p_quadIndex] = finalTangent;
	g_quadTangentInd[p_quadIndex].TangentIndex0 = p_quadIndex;
	g_quadTangentInd[p_quadIndex].TangentIndex1 = p_quadIndex;
	g_quadTangentInd[p_quadIndex].TangentIndex2 = p_quadIndex;
	g_quadTangentInd[p_quadIndex].TangentIndex3 = p_quadIndex;

	

	//quadNormal


}

// this one calculates one tangent per one face
void CalculateTangents1()
{
	g_nTangents = g_nQuads;
	for (int i = 0; i < g_nQuads; i++)
	{
		CalculateTangent1(i);
	}
}
